/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package analisadorSemantico;

import java.util.Stack;
import modelo.AtribuicaoIncompativel;
import modelo.ErroSemantico;
import modelo.TiposIncompativeis;
import modelo.WarningExpressao;
import modelo.tipos.TipoVariavel;
import static modelo.tipos.TipoVariavel.REAL;
import static modelo.tipos.TipoVariavel.STRING;

/**
 *
 * @author DEP
 */
public class VerificacaoDeTipos {

    private Stack<TipoVariavel> pilhaDeTipos;
    private int linha;

    public VerificacaoDeTipos(int linha) {
        this.pilhaDeTipos = new Stack<>();
        this.linha = linha;
    }

    public void push(TipoVariavel t) {
        pilhaDeTipos.push(t);
    }

    public TipoVariavel avaliarAtribuicao() throws ErroSemantico {
        try {
            TipoVariavel t1 = null, t2 = null;
            if (pilhaDeTipos.peek() != null) {
                t1 = pilhaDeTipos.pop();
            }
            while (!pilhaDeTipos.isEmpty() && t1 != null) {
                t2 = pilhaDeTipos.pop();
                switch (t1) {
                    case STRING:
                        if (t2 == TipoVariavel.INTEIRO) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.REAL) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                            continue;
                        }
                        break;

                    case CHAR:
                        if (t2 == TipoVariavel.INTEIRO) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            continue;
                        }
                        if (t2 == TipoVariavel.REAL) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                           continue;

                        }
                        break;

                    case INTEIRO:
                        if (t2 == TipoVariavel.REAL) {
                            continue;
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                            continue;
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        break;

                    case REAL:
                        if (t2 == TipoVariavel.REAL) {
                            continue;
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                           throw new WarningExpressao(t1, t2, linha);
                            
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                            throw new AtribuicaoIncompativel(t1, t2, linha);
                        }
                        break;
                }
            }
            return t1;
        } catch (NullPointerException ex) {
            throw new modelo.ErroSemantico() {
                public String errosToString() {
                    return "NullPointerException no metodo ChecagemDeTipos.avaliar()";
                }
            };
        } catch (ArrayIndexOutOfBoundsException ex) {
            throw new ErroSemantico() {
                public String errosToString() {
                    return "ArrayIndexOutOfBoundsException no metodo ChecagemDeTipos.avaliar()";
                }
            };
        }
    }

    public TipoVariavel avaliar() throws ErroSemantico {
        try {
            TipoVariavel t1 = null, t2 = null;
            if (!pilhaDeTipos.isEmpty() && pilhaDeTipos.peek() != null) {
                t1 = pilhaDeTipos.pop();
            }
            while (!pilhaDeTipos.isEmpty() && t1 != null) {
                t2 = pilhaDeTipos.pop();
                switch (t1) {
                    case STRING:
                        if (t2 == TipoVariavel.REAL) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                            continue;
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            t2 = TipoVariavel.STRING;
                            continue;
                        }
                        break;

                    case CHAR:
                        if (t2 == TipoVariavel.REAL) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        if (t2 == TipoVariavel.STRING) {
                            t1 = TipoVariavel.STRING;
                            continue;
                        }
                        if (t2 == TipoVariavel.CHAR) {
                            continue;
                        }
                        break;

                    case INTEIRO:
                        if (t2 == TipoVariavel.REAL) {
                            t1 = TipoVariavel.REAL;
                            continue;
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                            continue;
                        }
                        if (t2 == TipoVariavel.CHAR || t2 == TipoVariavel.STRING) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        break;

                    case REAL:
                        if (t2 == TipoVariavel.REAL) {
                            continue;
                        }
                        if (t2 == TipoVariavel.INTEIRO) {
                            t2 = TipoVariavel.REAL;
                            continue;
                         
                        }
                        if (t2 == TipoVariavel.CHAR || t2 == TipoVariavel.STRING) {
                            throw new TiposIncompativeis(t1, t2, linha);
                        }
                        break;
                }
            }
            return t1;
        } catch (NullPointerException ex) {
            throw new modelo.ErroSemantico() {
                public String errosToString() {
                    return "NullPointerException no metodo ChecagemDeTipos.avaliar()";
                }
            };
        } catch (ArrayIndexOutOfBoundsException ex) {
            throw new ErroSemantico() {
                public String errosToString() {
                    return "ArrayIndexOutOfBoundsException no metodo ChecagemDeTipos.avaliar()";
                }
            };
        }


    }
}
